﻿using System;
using System.Collections.Generic;
using System.Text;

namespace MiniStack
{
    class Program
    {
        enum Instrucciones : byte
        { 
            NOP,    // 1 byte
            GOTO,   // 1 byte | dir   = 1 byte
            PUSH,   // 1 byte | elem  = 1 byte
            POP,    // 1 byte
            ADD,    // 1 byte
            SUB,    // 1 byte
            PRINT,  // 1 byte
            RET,    // 1 byte
            CEQ,    // 1 byte | elem1 = 1 byte | elem2 = 1 byte
            BE,     // 1 byte | target= 1 byte
            BNE,    // 1 byte | target= 1 byte
        }

        static void Main(string[] args)
        {
            List<byte> programa = new List<byte>();

            ////suma dos elementos
            //programa.Add((byte)Instrucciones.PUSH);
            //programa.Add((byte)10);
            //programa.Add((byte)Instrucciones.PUSH);
            //programa.Add((byte)20);
            //programa.Add((byte)Instrucciones.ADD);
            //programa.Add((byte)Instrucciones.PRINT);
            //programa.Add((byte)Instrucciones.RET);

            ////compara dos elementos
            //programa.Add((byte)Instrucciones.PUSH);
            //programa.Add((byte)10);
            //programa.Add((byte)Instrucciones.PUSH);
            //programa.Add((byte)10);
            //programa.Add((byte)Instrucciones.CEQ);
            //programa.Add((byte)Instrucciones.PRINT);
            //programa.Add((byte)Instrucciones.RET);

            // compara dos números (10 y 11)
            //    si son iguales    muestra 77
            //    si son diferentes muestra 66
            programa.Add((byte)Instrucciones.PUSH); // 0
            programa.Add((byte)10);                 // 1
            programa.Add((byte)Instrucciones.PUSH); // 2
            programa.Add((byte)11);                 // 3
            programa.Add((byte)Instrucciones.CEQ);  // 4
            programa.Add((byte)Instrucciones.BNE);  // 5
            programa.Add((byte)5); // pc+=5         // 6
            programa.Add((byte)Instrucciones.PUSH); // 7
            programa.Add((byte)77);                 // 8
            programa.Add((byte)Instrucciones.PRINT);// 9
            programa.Add((byte)Instrucciones.RET);  // 10
            programa.Add((byte)Instrucciones.PUSH); // 11
            programa.Add((byte)66);                 // 12
            programa.Add((byte)Instrucciones.PRINT);// 13
            programa.Add((byte)Instrucciones.RET);  // 14
            
            Stack<byte> evstack = new Stack<byte>();
            int pc = 0;

            for (;;)
            {
                switch (programa[pc])
                { 
                    case (byte) Instrucciones.NOP:
                        {
                            pc++;
                        }
                        break;
                    case (byte) Instrucciones.GOTO:
                        {
                            pc++;
                            pc = programa[pc];
                        }
                        break;
                    case (byte) Instrucciones.PUSH:
                        {
                            pc++;
                            evstack.Push(programa[pc]);
                            pc++;
                        }
                        break;
                    case (byte)Instrucciones.POP:
                        {
                            evstack.Pop();
                            pc++;
                        }
                        break;
                    case (byte)Instrucciones.ADD:
                        {
                            byte b1 = evstack.Pop();
                            byte b2 = evstack.Pop();
                            evstack.Push((byte)(b1 + b2));
                            pc++;
                        }
                        break;
                    case (byte)Instrucciones.SUB:
                        {
                            byte b1 = evstack.Pop();
                            byte b2 = evstack.Pop();
                            evstack.Push((byte)(b1 - b2));
                            pc++;
                        }
                        break;
                    case (byte)Instrucciones.PRINT:
                        {
                            Console.WriteLine(evstack.Pop());
                            pc++;
                        }
                        break;
                    case (byte)Instrucciones.RET:
                        {
                            goto Fin;
                        }
                        break;
                    // el famoso compare if equal
                    case (byte)Instrucciones.CEQ: 
                        {
                            byte b1 = evstack.Pop();
                            byte b2 = evstack.Pop();
                            evstack.Push((byte)(b1 == b2 ? 1 : 0));
                            pc++;
                        }
                        break;
                    // el famoso branch if equal
                    case (byte)Instrucciones.BE: 
                        { 
                            byte val = evstack.Pop();

                            if (val != 0) {
                                pc++;
                                pc+= programa[pc];
                            } else {
                                pc++;
                                pc++;
                            }
                        }
                        break;
                    // el famoso "branch if not equal"
                    case (byte)Instrucciones.BNE: 
                        {
                            byte val = evstack.Pop();
                            
                            if (val == 0) {
                                pc++;
                                pc+= programa[pc];
                            } else {
                                pc++;
                                pc++;
                            }
                        }
                        break;
                }
            }

        Fin:
            {
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("Fin del programa");
                Console.ForegroundColor = ConsoleColor.Gray;
                Console.ReadLine();
            }
        }
    }
}
